# coding=utf-8

from base64 import encode
import os
from typing import Dict, List, Tuple
from generator.cpp.file_type import CPPHeader
from generator.cpp.types import EnumClass, EnumItem, NameSpace
from parse.parse_types import Module
from parse.sdb_parser import ECU
from utils.Common import BYTES, CBPA, INT32, INT32_VEC, READ, SIGNAL, WRITE
from utils.payload import EncodeType
import random

try:
    import CppHeaderParser
except :
    os.system('pip3 install robotpy-cppheaderparser')
    import CppHeaderParser

#解析当前项目中已有的sdb信号，返回信号id与名字的双向映射字典
def parseCurrentSignalIds()->Tuple[dict,dict]:
    curSignalIdsHeaderFile = "../transfer/libapvp/include/ap/signal_id.h"

    if not os.path.exists(curSignalIdsHeaderFile):
        print(">>>>>>>>>>>>not exist signal_id.h")
        return ({},{})
    signalHeader = CppHeaderParser.CppHeader(curSignalIdsHeaderFile)
    signalName2IdMap = {}
    for emitem in signalHeader.enums:
        emname = emitem["name"]
        if(emname.find("Signal") == -1):
            continue
        for sigitem in emitem["values"]:
            signalName2IdMap[sigitem["name"]] = sigitem["value"] & 0xffff
    signalId2NameMap = { v:k for k,v in signalName2IdMap.items() }
    if(signalId2NameMap.__len__() != signalName2IdMap.__len__()):
        diffSigNames = signalName2IdMap.keys() - signalId2NameMap.values()
        dupSigIds = set()
        for sigName in diffSigNames:
            dupSigIds.add(signalName2IdMap.get(sigName))
        print("dupids",dupSigIds)

        dupSigInfos = {}
        for sigName,sigId in signalName2IdMap.items():
            if (sigId not in dupSigIds):
                continue
            dupSigs = dupSigInfos.get(sigId, set())
            dupSigs.add(sigName)
            dupSigInfos[sigId] = dupSigs

        raise NameError("sdb信号id有重复:", dupSigInfos)

    return (signalId2NameMap,signalName2IdMap)

#根据信号名生成id,同时startIdx
#先从历史信号头文件中查找，如找到，返回id；
#否则从起始索引逐步查找不重复的id
def genSignalIdByName(startIdx:int , sigName:str,id2NameMap:Dict[int, str] = {}, 
                      name2IdMap:Dict[str,int] = {}, maxIdx:int =0xffff ):
    if(startIdx <= 0 or startIdx > maxIdx):
        startIdx = 1
    retIdx = name2IdMap.get(sigName,-1)
    if(retIdx != -1):
        #print(">>>>>>>%s 沿用原id: %d" % (sigName, retIdx))
        return (retIdx,startIdx)
    retIdx = startIdx
    while(retIdx in id2NameMap):
        #print(">>>>>>%d is used by sigName:%s,find next id" % (retIdx, id2NameMap[retIdx]))
        retIdx += 1
        if(retIdx > maxIdx):
            retIdx = 1
    print(">>>>>>sigName %s 新生成id:%d" % (sigName, retIdx))
    return (retIdx, retIdx+1)
    

def gen_SignalIdHeadFile(ecus: List[ECU], modules: List[Module],outDir:str):
    signal_h = CPPHeader("ecarx::transfer")
    signal_h.addInclude("cstdint")
    signal_h.addInclude("transfer/common.h", True)

    enumTX = EnumClass("SignalTX", "int32_t")
    enumRX = EnumClass("SignalRX", "int32_t")

    #收集生成的相关信号以及CBCP的EncodeType
    enumNames=[]
    enums:List[EncodeType]=[]
    anno = """
   /**
    *{0}
    *
    * Publisher         : {1}
    * Subscriber        : {2}
    *
    * {3}
    */"""
    (signalId2NameMap,signalName2IdMap) = parseCurrentSignalIds()
    # 先进行TX信号解析
    signalStartIdx = random.randint(1,0xffff)
    for ecu in ecus:

        for tx in ecu.txSignals:
            (signalId,signalStartIdx) = genSignalIdByName(startIdx=signalStartIdx, sigName=tx.name,
                                                          id2NameMap=signalId2NameMap,
                                                          name2IdMap=signalName2IdMap)
            (name,value) = tx.getSignalIdTypeDefine()
            enumTX.addEmunItem(EnumItem(tx.name, "{0}".format(hex(signalId | SIGNAL | WRITE | value)),
                                        anno.format(
                tx.signals[0].desc,
                "IHU",
                ecu.name,
                "{0}|SignalType::SIGNAL|SignalAccess::WRITE|ValueType::{1}".format(
                    hex(signalId), name)
            )
            ))
            for tx in tx.signals:
                if tx.hasEnum() :
                    encodeType=tx.getEncodeType()
                    if encodeType.getTypeName() not in enumNames:
                        enumNames.append(encodeType.getTypeName())
                        enums.append(encodeType)
    # 在进行RX信号解析
    for ecu in ecus:
        for rx in ecu.rxPackage.signals:
            (signalId,signalStartIdx) = genSignalIdByName(startIdx=signalStartIdx, sigName=rx.name,
                                                          id2NameMap=signalId2NameMap,
                                                          name2IdMap=signalName2IdMap)
            (name, value) = rx.getSignalIdTypeDefine()
            enumRX.addEmunItem(EnumItem(rx.name, "{0}".format(hex(signalId | SIGNAL | WRITE | value)),
                                        anno.format(
                rx.desc,
                ecu.name,
                "IHU",
                "{0}|SignalType::SIGNAL|SignalAccess::READ|{1}".format(
                    hex(signalId), name)
            )
            ))
            if rx.hasEnum():
                encodeType=rx.getEncodeType()
                if encodeType.getTypeName() not in enumNames:
                    enumNames.append(encodeType.getTypeName())
                    enums.append(encodeType)

    signal_h.addMember(enumTX)
    signal_h.addMember(enumRX)

    enumPA = EnumClass("PASignal", "int32_t")
    enumCB = EnumClass("CBSignal", "int32_t")
    paAnno = """
    /**
    * {0}
    *
    * Module:  {1}
    *
    * {2}
    */"""
    for module in modules:
        for pa in module.pa_apis:
            (signalId,signalStartIdx) = genSignalIdByName(startIdx=signalStartIdx, sigName=pa.api,
                                                          id2NameMap=signalId2NameMap,
                                                          name2IdMap=signalName2IdMap)
            enumPA.addEmunItem(EnumItem(
                pa.api,
                "{0}".format(hex(signalId | CBPA | READ | BYTES)),
                paAnno.format(
                    pa.desc, module.name, "{0}|SignalType::CBPA|SignalAccess::READ|ValueType::BYTES".format(hex(signalId)))
            ))
            if pa.getPayload().isStructPayload():
                structPayload = pa.getPayload().getStructPayload()
                for member in structPayload.members:
                    if member.hasEncodeType() and member.getEncodeType().isEnum():
                        encodeType = member.getEncodeType()
                        if encodeType.getTypeName() not in enumNames:
                            enumNames.append(encodeType.getTypeName())
                            enums.append(encodeType)
            
        for cb in module.cb_apis:
            (signalId,signalStartIdx) = genSignalIdByName(startIdx=signalStartIdx, sigName=cb.api,
                                                          id2NameMap=signalId2NameMap,
                                                          name2IdMap=signalName2IdMap)
            payload = cb.getPayload()
            if payload.isStructPayload():
                enumCB.addEmunItem(EnumItem(
                    cb.api,
                    "{0}".format(hex(signalId | CBPA | WRITE | BYTES)),
                    paAnno.format(cb.desc, module.name, "{0}|SignalType::CBPA|SignalAccess::WRITE|ValueType::BYTES".format(hex(signalId)))
                ))
            elif payload.isArrayPayload():
                enumCB.addEmunItem(EnumItem(
                    cb.api,
                    "{0}".format(hex(signalId | CBPA | WRITE | INT32_VEC)),
                    paAnno.format(cb.desc, module.name, "{0}|SignalType::CBPA|SignalAccess::WRITE|ValueType::INT32_VEC".format(hex(signalId)))
                ))
            else:
                enumCB.addEmunItem(EnumItem(
                    cb.api,
                    "{0}".format(hex(signalId | CBPA | WRITE | INT32)),
                    paAnno.format(cb.desc, module.name, "{0}|SignalType::CBPA|SignalAccess::WRITE|ValueType::INT32".format(hex(signalId)))
                ))
                basePayload=payload.getBaseTypePayload()
                if basePayload.hasEncodeType() and basePayload.getEncodeType().isEnum():
                    encodeType = basePayload.getEncodeType()
                    if encodeType.getTypeName() not in enumNames:
                        enumNames.append(encodeType.getTypeName())
                        enums.append(encodeType)

    signal_h.addMember(enumPA)
    signal_h.addMember(enumCB)
    # 信号值的类型 创建enum class 声明
    namespace=NameSpace("sdb")
    for encodeType in enums:
        enumClass = EnumClass(encodeType.getTypeName(), "int32_t")
        for (member,_) in encodeType.members:
            (name,value) = member.split("=")
            enumClass.addEmunItem(EnumItem(name,value))
        namespace.addTemplete(enumClass)
    signal_h.addMember(namespace)
    
    signal_h.toFile(os.path.join(outDir,"include/ap"), "signal_id.h")


